import cv2
import dlib

print(cv2.__version__)
print(dlib.__version__)
4.5.3
19.22.1
import os 
import cv2 
import matplotlib.pyplot as plt
import numpy as np
import dlib 

일반적인 사진으로 고양이 수염 붙이기

my_image_path = os.getenv('HOME') + '/aiffel/camera_sticker/images/IMG_0436.JPG'
img_bgr = cv2.imread(my_image_path)    # OpenCV로 이미지를 불러옴.
img_show = img_bgr.copy()      # 출력용 이미지를 따로 보관
plt.imshow(img_bgr) # 이미지를 출력하기 위해 출력할 이미지를 올려줌.
plt.show() # 이미지를 출력

# 색을 BGR 에서 RGB 변경
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
plt.imshow(img_rgb)
plt.show()

detector_hog = dlib.get_frontal_face_detector() # 기본 얼굴 감지기를 반환
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
dlib_rects = detector_hog(img_rgb, 1)  
# 찾은 얼굴 영역 박스 리스트
print(dlib_rects)   

for dlib_rect in dlib_rects: # 찾은 얼굴 영역의 좌표
    l = dlib_rect.left() # 왼쪽
    t = dlib_rect.top() # 위쪽
    r = dlib_rect.right() # 오른쪽
    b = dlib_rect.bottom() # 아래쪽

    cv2.rectangle(img_show, (l,t), (r,b), (0,255,0), 2, lineType=cv2.LINE_AA) # 시작점의 좌표와 종료점 좌표로 직각 사각형을 그림

img_show_rgb =  cv2.cvtColor(img_show, cv2.COLOR_BGR2RGB)
plt.imshow(img_show_rgb)
plt.show()
rectangles[[(2321, 1150) (3279, 2108)]]

model_path = os.getenv('HOME') + '/aiffel/camera_sticker/models/shape_predictor_68_face_landmarks.dat'
landmark_predictor = dlib.shape_predictor(model_path)
list_landmarks = []
for dlib_rect in dlib_rects:
    points = landmark_predictor(img_rgb, dlib_rect)
    list_points = list(map(lambda p: (p.x, p.y), points.parts()))
    list_landmarks.append(list_points)

print(len(list_landmarks[0]))
68
for landmark in list_landmarks:
    for point in landmark:
        cv2.circle(img_show, point, 10, (0, 255, 255), -1)
        
img_show_rgb = cv2.cvtColor(img_show, cv2.COLOR_BGR2RGB)
plt.imshow(img_show_rgb)
plt.show()

for dlib_rect, landmark in zip(dlib_rects, list_landmarks): # 얼굴 영역을 저장하고 있는 값과 68개의 랜드마크를 저장하고 있는 값으로 반복문 실행
    print (landmark[33]) # 코의 index는 33 입니다
    x = landmark[33][0] # 이미지에서 코 부위의 x값
    y = landmark[33][1]# 이미지에서 코 부위의 y값
    w = h = dlib_rect.width() # 얼굴 사각형의 가로와 세로를 가지고 고양이 수염의 이미지 변경
    print ('(x,y) : (%d,%d)'%(x,y))
    print ('(w,h) : (%d,%d)'%(w,h))
(2790, 1805)
(x,y) : (2790,1805)
(w,h) : (959,959)
sticker_path = os.getenv('HOME')+'/aiffel/camera_sticker/images/cat-whiskers.png' # 고양이 수염 경로
img_sticker = cv2.imread(sticker_path) # 스티커 이미지를 불러옵니다
img_sticker = cv2.resize(img_sticker, (w,h)) # 스티커 이미지 조정
print (img_sticker.shape) # 사이즈를 조정한 고양이 수염 이미지의 차원 확인
(959, 959, 3)
# 고양이 수염의 x, y를 시작점을 다시 잡아줌.
refined_x = x - w // 2 
refined_y = y - h //2 
print ('(x,y) : (%d,%d)'%(refined_x, refined_y)) 
(x,y) : (2311,1326)
# 고양이 수염 스티커 붙임.
sticker_area = img_bgr[refined_y:refined_y +img_sticker.shape[0], refined_x:refined_x+img_sticker.shape[1]]
img_bgr[refined_y:refined_y +img_sticker.shape[0], refined_x:refined_x+img_sticker.shape[1]] = \
    np.where(img_sticker==0,img_sticker,sticker_area).astype(np.uint8)
plt.imshow(cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB))
plt.show()

고양이 수염 흐리게 붙이기.

my_image_path = os.getenv('HOME') + '/aiffel/camera_sticker/images/IMG_0605.JPG'
img_bgr = cv2.imread(my_image_path)   
img_show = img_bgr.copy()      
plt.imshow(img_bgr)
plt.show() 

img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
plt.imshow(img_rgb) 
plt.show()

detector_hog = dlib.get_frontal_face_detector()
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
dlib_rects = detector_hog(img_rgb, 1)  
print(dlib_rects)   

for dlib_rect in dlib_rects: # 찾은 얼굴 영역의 좌표
    l = dlib_rect.left() # 왼쪽
    t = dlib_rect.top() # 위쪽
    r = dlib_rect.right() # 오른쪽
    b = dlib_rect.bottom() # 아래쪽

    cv2.rectangle(img_show, (l,t), (r,b), (0,255,0), 2, lineType=cv2.LINE_AA) # 시작점의 좌표와 종료점 좌표로 직각 사각형을 그림

img_show_rgb =  cv2.cvtColor(img_show, cv2.COLOR_BGR2RGB)
plt.imshow(img_show_rgb)
plt.show()
rectangles[[(2147, 613) (3297, 1764)]]

model_path = os.getenv('HOME') + '/aiffel/camera_sticker/models/shape_predictor_68_face_landmarks.dat'
landmark_predictor = dlib.shape_predictor(model_path)
list_landmarks = []
for dlib_rect in dlib_rects:
    points = landmark_predictor(img_rgb, dlib_rect)
    list_points = list(map(lambda p: (p.x, p.y), points.parts()))
    list_landmarks.append(list_points)

print(len(list_landmarks[0]))
68
for landmark in list_landmarks:
    for point in landmark:
        cv2.circle(img_show, point, 10, (0, 255, 255), -1)          
img_show_rgb = cv2.cvtColor(img_show, cv2.COLOR_BGR2RGB)
    # RGB 이미지로 전환
plt.imshow(img_show_rgb)
    # 이미지를 준비
plt.show()
    # 이미지를 출력

for dlib_rect, landmark in zip(dlib_rects, list_landmarks): # 얼굴 영역을 저장하고 있는 값과 68개의 랜드마크를 저장하고 있는 값으로 반복문 실행
    print (landmark[33]) 
    x = landmark[33][0] 
    y = landmark[33][1]
    w =h= dlib_rect.width()
    print ('(x,y) : (%d,%d)'%(x,y))
    print ('(w,h) : (%d,%d)'%(w,h))
(2694, 1383)
(x,y) : (2694,1383)
(w,h) : (1151,1151)
sticker_path = os.getenv('HOME')+'/aiffel/camera_sticker/images/cat-whiskers.png'
img_sticker = cv2.imread(sticker_path) 
img_sticker = cv2.resize(img_sticker, (w,h)) 
print (img_sticker.shape) 
(1151, 1151, 3)
refined_x = x - w // 2 
refined_y = y - h //2 
print ('(x,y) : (%d,%d)'%(refined_x, refined_y)) 
(x,y) : (2119,808)

흐리게 만들기

  • cv2.addWeighted(sticker_area,0.5,np.where(img_sticker==0,img_sticker,sticker_area).astype(np.uint8), 0.5, 0)을 사용해서 흐리게 만들었다.
sticker_area = img_bgr[refined_y:refined_y +img_sticker.shape[0], refined_x:refined_x+img_sticker.shape[1]]
img_bgr[refined_y:refined_y +img_sticker.shape[0], refined_x:refined_x+img_sticker.shape[1]] = \
        cv2.addWeighted(sticker_area,0.5,np.where(img_sticker==0,img_sticker,sticker_area).astype(np.uint8), 0.5, 0)
plt.imshow(cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)) 
plt.show()

측면 사진으로 고양이 수염 붙이기

my_image_path = os.getenv('HOME') + '/aiffel/camera_sticker/images/IMG_0889.JPG' 
img_bgr = cv2.imread(my_image_path)    
img_show = img_bgr.copy()      
plt.imshow(img_bgr) 
plt.show() 

img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
plt.imshow(img_rgb) 
plt.show()

detector_hog = dlib.get_frontal_face_detector() 
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
dlib_rects = detector_hog(img_rgb, 1)  
print(dlib_rects)   

for dlib_rect in dlib_rects: # 찾은 얼굴 영역의 좌표
    l = dlib_rect.left() # 왼쪽
    t = dlib_rect.top() # 위쪽
    r = dlib_rect.right() # 오른쪽
    b = dlib_rect.bottom() # 아래쪽

    cv2.rectangle(img_show, (l,t), (r,b), (0,255,0), 2, lineType=cv2.LINE_AA) # 시작점의 좌표와 종료점 좌표로 직각 사각형을 그림

img_show_rgb =  cv2.cvtColor(img_show, cv2.COLOR_BGR2RGB)
plt.imshow(img_show_rgb)
plt.show()
rectangles[[(2013, 1211) (2281, 1479)]]

model_path = os.getenv('HOME') + '/aiffel/camera_sticker/models/shape_predictor_68_face_landmarks.dat'
landmark_predictor = dlib.shape_predictor(model_path)
list_landmarks = []
for dlib_rect in dlib_rects:
    points = landmark_predictor(img_rgb, dlib_rect)
    list_points = list(map(lambda p: (p.x, p.y), points.parts()))
    list_landmarks.append(list_points)
print(len(list_landmarks[0]))
68
for landmark in list_landmarks:
    for point in landmark:
        cv2.circle(img_show, point, 10, (0, 255, 255), -1)
img_show_rgb = cv2.cvtColor(img_show, cv2.COLOR_BGR2RGB)
plt.imshow(img_show_rgb)
plt.show()

for dlib_rect, landmark in zip(dlib_rects, list_landmarks):
    print (landmark[33])
    x = landmark[33][0] 
    y = landmark[33][1]
    w =h= dlib_rect.width() 
    print ('(x,y) : (%d,%d)'%(x,y))
    print ('(w,h) : (%d,%d)'%(w,h))
(2174, 1372)
(x,y) : (2174,1372)
(w,h) : (269,269)
sticker_path = os.getenv('HOME')+'/aiffel/camera_sticker/images/cat-whiskers.png' 
img_sticker = cv2.imread(sticker_path) 
img_sticker = cv2.resize(img_sticker, (w,h))
print (img_sticker.shape)
(269, 269, 3)
refined_x = x - w // 2 
refined_y = y - h //2 
print ('(x,y) : (%d,%d)'%(refined_x, refined_y)) 
(x,y) : (2040,1238)
sticker_area = img_bgr[refined_y:refined_y +img_sticker.shape[0], refined_x:refined_x+img_sticker.shape[1]]
img_bgr[refined_y:refined_y +img_sticker.shape[0], refined_x:refined_x+img_sticker.shape[1]] = \
    np.where(img_sticker==0,img_sticker,sticker_area).astype(np.uint8)
plt.imshow(cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB))
plt.show()

  • 측면으로 했을때, 살짝 고양이 수염이 옆으로 틀어지지 않았지만 멀리있는 얼굴이라서 조금 괜찮아 보였던 것 같다.

  • 광대 쪽에 있는 landmark 사이의 길이 비율을 보고 고양이 수염의 높이의 비율을 조정하면 입체적으로 고양이 수염이 붙여질 거라 생각한다.

고개를 숙인 사진(얼굴 각도)에 고양이 수염 붙이기

my_image_path = os.getenv('HOME') + '/aiffel/camera_sticker/images/IMG_1075.JPG' 
img_bgr = cv2.imread(my_image_path) 
img_show = img_bgr.copy()     
plt.imshow(img_bgr) 
plt.show() 

detector_hog = dlib.get_frontal_face_detector()
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
dlib_rects = detector_hog(img_rgb, 1)  
print(dlib_rects)   

for dlib_rect in dlib_rects: # 찾은 얼굴 영역의 좌표
    l = dlib_rect.left() # 왼쪽
    t = dlib_rect.top() # 위쪽
    r = dlib_rect.right() # 오른쪽
    b = dlib_rect.bottom() # 아래쪽

    cv2.rectangle(img_show, (l,t), (r,b), (0,255,0), 2, lineType=cv2.LINE_AA)

img_show_rgb =  cv2.cvtColor(img_show, cv2.COLOR_BGR2RGB)
plt.imshow(img_show_rgb)
plt.show()
rectangles[[(1529, 1016) (1991, 1478)]]

model_path = os.getenv('HOME') + '/aiffel/camera_sticker/models/shape_predictor_68_face_landmarks.dat'
landmark_predictor = dlib.shape_predictor(model_path)
list_landmarks = []

for dlib_rect in dlib_rects:
    points = landmark_predictor(img_rgb, dlib_rect)
    list_points = list(map(lambda p: (p.x, p.y), points.parts()))
    list_landmarks.append(list_points)

print(len(list_landmarks[0]))
68
for landmark in list_landmarks:
    for point in landmark:
        cv2.circle(img_show, point, 10, (0, 255, 255), -1)
           
img_show_rgb = cv2.cvtColor(img_show, cv2.COLOR_BGR2RGB)
plt.imshow(img_show_rgb)
plt.show()

for dlib_rect, landmark in zip(dlib_rects, list_landmarks): 
    print (landmark[33]) 
    x = landmark[33][0] 
    y = landmark[33][1]
    w =h= dlib_rect.width()
    print ('(x,y) : (%d,%d)'%(x,y))
    print ('(w,h) : (%d,%d)'%(w,h))
import math
tan = (landmark[30][0]-landmark[27][0])/(landmark[30][1]-landmark[27][1])
theta = np.arctan(tan)
rotate_angle = theta *180/math.pi
print(rotate_angle)
(1764, 1307)
(x,y) : (1764,1307)
(w,h) : (463,463)
-30.183680657222354
sticker_path = os.getenv('HOME')+'/aiffel/camera_sticker/images/cat-whiskers.png' 
img_sticker = cv2.imread(sticker_path) 
img_sticker = cv2.resize(img_sticker, (w,h)) 
print (img_sticker.shape) 
(463, 463, 3)
refined_x = x - w // 2 
refined_y = y - h //2
print ('(x,y) : (%d,%d)'%(refined_x, refined_y))
sticker_area = img_bgr[refined_y:refined_y +img_sticker.shape[0], refined_x:refined_x+img_sticker.shape[1]]
img_bgr[refined_y:refined_y +img_sticker.shape[0], refined_x:refined_x+img_sticker.shape[1]] = \
    np.where(img_sticker==0,img_sticker,sticker_area).astype(np.uint8)
plt.imshow(cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)) 
plt.show()
(x,y) : (1533,1076)

  • 회전 함수를 적용하지 않을 때, 고양이 수염이 제대로 붙여있지 않다.
import math
tan = (landmark[30][0]-landmark[27][0])/(landmark[30][1]-landmark[27][1])
theta = np.arctan(tan)
rotate_angle = theta *180/math.pi
print(rotate_angle)
-30.183680657222354
def rotate_image(image, angle):
  image_center = tuple(np.array(image.shape[1::-1]) / 2)
  rot_mat = cv2.getRotationMatrix2D(image_center, angle, 1.0)
  result = cv2.warpAffine(image, rot_mat, image.shape[1::-1], flags=cv2.INTER_LINEAR,borderValue=(255,255,255))
  return result

img_rotate = rotate_image(img_sticker,rotate_angle)
print(img_rotate.shape)
plt.imshow(img_rotate)
plt.show()
(463, 463, 3)

refined_x = x - w // 2
refined_y = y - h//2 

print ('(x,y) : (%d,%d)'%(refined_x, refined_y))
sticker_area = img_bgr[refined_y:refined_y +img_rotate.shape[0], refined_x:refined_x+img_rotate.shape[1]]
img_bgr[refined_y:refined_y +img_rotate.shape[0], refined_x:refined_x+img_rotate.shape[1]] = np.where(img_rotate==255,sticker_area, img_rotate).astype(np.uint8)
plt.imshow(cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB))
plt. show()
(x,y) : (1533,1076)

  • 회전 함수(rotate_image)를 만들어 코의 기둥을 tan값을 구하고 그 각을 알기위해 arctan을 구했다. 그 각도 만큼 고양이 수염을 회전시켜 고개의 각도에 따라 고양이 수염이 회전하여 붙여지는 것을 확인할 수 있다.

어두운 사진에 고양이 수염 붙이기

my_image_path = os.getenv('HOME') + '/aiffel/camera_sticker/images/IMG_0952.JPG'
img_bgr = cv2.imread(my_image_path)    
img_show = img_bgr.copy()      
plt.imshow(img_bgr) 
plt.show() 

detector_hog = dlib.get_frontal_face_detector()
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
dlib_rects = detector_hog(img_rgb, 1)  
print(dlib_rects)   

for dlib_rect in dlib_rects: # 찾은 얼굴 영역의 좌표
    l = dlib_rect.left() # 왼쪽
    t = dlib_rect.top() # 위쪽
    r = dlib_rect.right() # 오른쪽
    b = dlib_rect.bottom() # 아래쪽

    cv2.rectangle(img_show, (l,t), (r,b), (0,255,0), 2, lineType=cv2.LINE_AA) # 시작점의 좌표와 종료점 좌표로 직각 사각형을 그림

img_show_rgb =  cv2.cvtColor(img_show, cv2.COLOR_BGR2RGB)
plt.imshow(img_show_rgb)
plt.show()
rectangles[[(2844, 1311) (3165, 1632)], [(2193, 1326) (2229, 1362)]]

  • 위에서 사각형이 2개가 나온 것을 확인하고, 어둡고 복잡한 배경때문에 얼굴로 인식했을 수 있겠다는 생각을 했다.
model_path = os.getenv('HOME') + '/aiffel/camera_sticker/models/shape_predictor_68_face_landmarks.dat'
landmark_predictor = dlib.shape_predictor(model_path)
list_landmarks = []
for dlib_rect in dlib_rects:
    points = landmark_predictor(img_rgb, dlib_rect)
    list_points = list(map(lambda p: (p.x, p.y), points.parts()))
    list_landmarks.append(list_points)

print(len(list_landmarks[0]))
68
# 배경 이미지에도 landmark가 새겨질 것이다.
for landmark in list_landmarks:
    for point in landmark:
        cv2.circle(img_show, point, 10, (0, 255, 255), -1)
img_show_rgb = cv2.cvtColor(img_show, cv2.COLOR_BGR2RGB)
plt.imshow(img_show_rgb)
plt.show()

sticker_path = os.getenv('HOME')+'/aiffel/camera_sticker/images/cat-whiskers.png' 
img_sticker = cv2.imread(sticker_path) 
for dlib_rect, landmark in zip(dlib_rects, list_landmarks): 
    print (landmark[33]) 
    x = landmark[33][0] 
    y = landmark[33][1]
    w =h= dlib_rect.width() 
    print ('(x,y) : (%d,%d)'%(x,y))
    print ('(w,h) : (%d,%d)'%(w,h))
    refined_x = x - w // 2 
    refined_y = y - h //2
    img_sticker = cv2.resize(img_sticker, (w,h))
    print (img_sticker.shape) 
    print ('(x,y) : (%d,%d)'%(refined_x, refined_y))
    sticker_area = img_bgr[refined_y:refined_y +img_sticker.shape[0], refined_x:refined_x+img_sticker.shape[1]]
    img_bgr[refined_y:refined_y +img_sticker.shape[0], refined_x:refined_x+img_sticker.shape[1]] = \
    np.where(img_sticker==0,img_sticker,sticker_area).astype(np.uint8)
plt.imshow(cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB))
plt.show()
(3004, 1522)
(x,y) : (3004,1522)
(w,h) : (322,322)
(322, 322, 3)
(x,y) : (2843,1361)
(2215, 1351)
(x,y) : (2215,1351)
(w,h) : (37,37)
(37, 37, 3)
(x,y) : (2197,1333)

  • 배경이미지에 사람 얼굴을 인식해 반복문을 넣어 실제 얼굴에 고양이 수염을 넣을 수 있었다.

멀리있는 얼굴에 고양이 수염 붙이기

my_image_path = os.getenv('HOME') + '/aiffel/camera_sticker/images/IMG_0882.JPG' 
img_bgr = cv2.imread(my_image_path)
img_show = img_bgr.copy()      
plt.imshow(img_bgr) 
plt.show() 

detector_hog = dlib.get_frontal_face_detector()
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
dlib_rects = detector_hog(img_rgb, 1)  
print(dlib_rects)   

for dlib_rect in dlib_rects: # 찾은 얼굴 영역의 좌표
    l = dlib_rect.left() # 왼쪽
    t = dlib_rect.top() # 위쪽
    r = dlib_rect.right() # 오른쪽
    b = dlib_rect.bottom() # 아래쪽

    cv2.rectangle(img_show, (l,t), (r,b), (0,255,0), 2, lineType=cv2.LINE_AA) # 시작점의 좌표와 종료점 좌표로 직각 사각형을 그림

img_show_rgb =  cv2.cvtColor(img_show, cv2.COLOR_BGR2RGB)
plt.imshow(img_show_rgb)
plt.show()
rectangles[[(1480, 1460) (1666, 1646)]]

model_path = os.getenv('HOME') + '/aiffel/camera_sticker/models/shape_predictor_68_face_landmarks.dat'
landmark_predictor = dlib.shape_predictor(model_path)
list_landmarks = []
for dlib_rect in dlib_rects:
    points = landmark_predictor(img_rgb, dlib_rect)
    list_points = list(map(lambda p: (p.x, p.y), points.parts()))
    list_landmarks.append(list_points)

print(len(list_landmarks[0]))
68
for landmark in list_landmarks:
    for point in landmark:
        cv2.circle(img_show, point, 10, (0, 255, 255), -1)
img_show_rgb = cv2.cvtColor(img_show, cv2.COLOR_BGR2RGB)
plt.imshow(img_show_rgb)
plt.show()

sticker_path = os.getenv('HOME')+'/aiffel/camera_sticker/images/cat-whiskers.png'
img_sticker = cv2.imread(sticker_path)
for dlib_rect, landmark in zip(dlib_rects, list_landmarks): 
    print (landmark[33]) 
    x = landmark[33][0] 
    y = landmark[33][1]
    w =h= dlib_rect.width() 
    print ('(x,y) : (%d,%d)'%(x,y))
    print ('(w,h) : (%d,%d)'%(w,h))
    refined_x = x - w // 2 
    refined_y = y - h //2
    img_sticker = cv2.resize(img_sticker, (w,h))
    print (img_sticker.shape) 
    print ('(x,y) : (%d,%d)'%(refined_x, refined_y))
    sticker_area = img_bgr[refined_y:refined_y +img_sticker.shape[0], refined_x:refined_x+img_sticker.shape[1]]
    img_bgr[refined_y:refined_y +img_sticker.shape[0], refined_x:refined_x+img_sticker.shape[1]] = \
    np.where(img_sticker==0,img_sticker,sticker_area).astype(np.uint8)
plt.imshow(cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)) 
plt.show()
(1567, 1573)
(x,y) : (1567,1573)
(w,h) : (187,187)
(187, 187, 3)
(x,y) : (1474,1480)

  • 멀리있는 사진도 고양이 수염이 얼굴 사이즈에 맞게 잘 붙는 것 같다.

두 명 사진에 고양이 수염 붙이기

my_image_path = os.getenv('HOME') + '/aiffel/camera_sticker/images/IMG_9599.JPG'
img_bgr = cv2.imread(my_image_path)   
img_show = img_bgr.copy()    
plt.imshow(img_bgr)
plt.show() 

detector_hog = dlib.get_frontal_face_detector()
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
dlib_rects = detector_hog(img_rgb, 1)  
print(dlib_rects)   

for dlib_rect in dlib_rects: # 찾은 얼굴 영역의 좌표
    l = dlib_rect.left() # 왼쪽
    t = dlib_rect.top() # 위쪽
    r = dlib_rect.right() # 오른쪽
    b = dlib_rect.bottom() # 아래쪽

    cv2.rectangle(img_show, (l,t), (r,b), (0,255,0), 3, lineType=cv2.LINE_AA) # 시작점의 좌표와 종료점 좌표로 직각 사각형을 그림

img_show_rgb =  cv2.cvtColor(img_show, cv2.COLOR_BGR2RGB)
plt.imshow(img_show_rgb)
plt.show()
rectangles[[(191, 937) (1149, 1895)], [(1632, 1067) (2094, 1530)]]

model_path = os.getenv('HOME') + '/aiffel/camera_sticker/models/shape_predictor_68_face_landmarks.dat'
landmark_predictor = dlib.shape_predictor(model_path)
list_landmarks = []
for dlib_rect in dlib_rects:
    points = landmark_predictor(img_rgb, dlib_rect)
    list_points = list(map(lambda p: (p.x, p.y), points.parts()))
    list_landmarks.append(list_points)

print(len(list_landmarks[0]))
68
for landmark in list_landmarks:
    for point in landmark:
        cv2.circle(img_show, point, 10, (0, 255, 255), -1)
img_show_rgb = cv2.cvtColor(img_show, cv2.COLOR_BGR2RGB)
plt.imshow(img_show_rgb)
plt.show()

sticker_path = os.getenv('HOME')+'/aiffel/camera_sticker/images/cat-whiskers.png'
img_sticker = cv2.imread(sticker_path) 
for dlib_rect, landmark in zip(dlib_rects, list_landmarks):
    print (landmark[33]) 
    x = landmark[33][0] 
    y = landmark[33][1]
    w =h= dlib_rect.width() 
    print ('(x,y) : (%d,%d)'%(x,y))
    print ('(w,h) : (%d,%d)'%(w,h))
    img_sticker = cv2.resize(img_sticker, (w,h))
    tan = (landmark[30][0]-landmark[27][0])/(landmark[30][1]-landmark[27][1])
    theta = np.arctan(tan)
    rotate_angle = theta *180/math.pi
    print(rotate_angle)
    img_rotate = rotate_image(img_sticker,rotate_angle)
    
    refined_x = x - w // 2 
    refined_y = y - h //2
    print ('(x,y) : (%d,%d)'%(refined_x, refined_y))
    sticker_area = img_bgr[refined_y:refined_y +img_rotate.shape[0], refined_x:refined_x+img_rotate.shape[1]]
    img_bgr[refined_y:refined_y +img_rotate.shape[0], refined_x:refined_x+img_rotate.shape[1]] = \
    cv2.addWeighted(sticker_area,0.5,np.where(img_rotate==0,img_rotate,sticker_area).astype(np.uint8), 0.5, 0)
plt.imshow(cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB))
plt.show()
(690, 1569)
(x,y) : (690,1569)
(w,h) : (959,959)
-8.866676044144272
(x,y) : (211,1090)
(1849, 1374)
(x,y) : (1849,1374)
(w,h) : (463,463)
-16.886791123944043
(x,y) : (1618,1143)

  • 얼굴의 각도에 따라 고양이 수염을 회전시켜서 붙였다.

세 명 사진에 고양이 수염 붙이기

my_image_path = os.getenv('HOME') + '/aiffel/camera_sticker/images/IMG_2569.JPG' 
img_bgr = cv2.imread(my_image_path)    
img_show = img_bgr.copy()      
plt.imshow(img_bgr) 
plt.show()

detector_hog = dlib.get_frontal_face_detector()
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
dlib_rects = detector_hog(img_rgb, 1)  
print(dlib_rects)   

for dlib_rect in dlib_rects: # 찾은 얼굴 영역의 좌표
    l = dlib_rect.left() # 왼쪽
    t = dlib_rect.top() # 위쪽
    r = dlib_rect.right() # 오른쪽
    b = dlib_rect.bottom() # 아래쪽

    cv2.rectangle(img_show, (l,t), (r,b), (0,255,0), 3, lineType=cv2.LINE_AA)

img_show_rgb =  cv2.cvtColor(img_show, cv2.COLOR_BGR2RGB)
plt.imshow(img_show_rgb)
plt.show()
rectangles[[(740, 491) (1061, 812)], [(1256, 724) (2214, 1682)], [(247, 418) (632, 804)]]

model_path = os.getenv('HOME') + '/aiffel/camera_sticker/models/shape_predictor_68_face_landmarks.dat'
landmark_predictor = dlib.shape_predictor(model_path)
list_landmarks = []
for dlib_rect in dlib_rects:
    points = landmark_predictor(img_rgb, dlib_rect)
    list_points = list(map(lambda p: (p.x, p.y), points.parts()))
    list_landmarks.append(list_points)

print(len(list_landmarks[0]))
68
for landmark in list_landmarks:
    for point in landmark:
        cv2.circle(img_show, point, 10, (0, 255, 255), -1)
img_show_rgb = cv2.cvtColor(img_show, cv2.COLOR_BGR2RGB)
plt.imshow(img_show_rgb)
plt.show()

sticker_path = os.getenv('HOME')+'/aiffel/camera_sticker/images/cat-whiskers.png' 
img_sticker = cv2.imread(sticker_path)

고양이 수염을 회전 시켰을 때 사진

for dlib_rect, landmark in zip(dlib_rects, list_landmarks):
    print (landmark[33]) 
    x = landmark[33][0] 
    y = landmark[33][1]
    w =h= dlib_rect.width() 
    print ('(x,y) : (%d,%d)'%(x,y))
    print ('(w,h) : (%d,%d)'%(w,h))
    img_sticker = cv2.resize(img_sticker, (w,h))
    tan = (landmark[30][0]-landmark[27][0])/(landmark[30][1]-landmark[27][1])
    theta = np.arctan(tan)
    rotate_angle = theta *180/math.pi
    print(rotate_angle)
    img_rotate = rotate_image(img_sticker,rotate_angle)
    
    refined_x = x - w // 2 
    refined_y = y - h //2
    print ('(x,y) : (%d,%d)'%(refined_x, refined_y))
    sticker_area = img_bgr[refined_y:refined_y +img_rotate.shape[0], refined_x:refined_x+img_rotate.shape[1]]
    img_bgr[refined_y:refined_y +img_rotate.shape[0], refined_x:refined_x+img_rotate.shape[1]] = \
    cv2.addWeighted(sticker_area,0.5,np.where(img_rotate==0,img_rotate,sticker_area).astype(np.uint8), 0.5, 0)
plt.imshow(cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB))
plt.show()
(902, 669)
(x,y) : (902,669)
(w,h) : (322,322)
-4.1849161251184155
(x,y) : (741,508)
(1746, 1257)
(x,y) : (1746,1257)
(w,h) : (959,959)
-4.283818526272696
(x,y) : (1267,778)
(482, 669)
(x,y) : (482,669)
(w,h) : (386,386)
-3.6522227803063356
(x,y) : (289,476)

고양이 수염을 회전 시키지 않았을 때 사진

for dlib_rect, landmark in zip(dlib_rects, list_landmarks):
    print (landmark[33]) 
    x = landmark[33][0] 
    y = landmark[33][1]
    w =h= dlib_rect.width() 
    print ('(x,y) : (%d,%d)'%(x,y))
    print ('(w,h) : (%d,%d)'%(w,h))
    img_sticker = cv2.resize(img_sticker, (w,h))
    #tan = (landmark[30][0]-landmark[27][0])/(landmark[30][1]-landmark[27][1])
    #theta = np.arctan(tan)
    #rotate_angle = theta *180/math.pi
    #print(rotate_angle)
    #img_rotate = rotate_image(img_sticker,rotate_angle)
    
    refined_x = x - w // 2 
    refined_y = y - h //2
    print ('(x,y) : (%d,%d)'%(refined_x, refined_y))
    sticker_area = img_bgr[refined_y:refined_y +img_sticker.shape[0], refined_x:refined_x+img_sticker.shape[1]]
    img_bgr[refined_y:refined_y +img_sticker.shape[0], refined_x:refined_x+img_sticker.shape[1]] = \
    cv2.addWeighted(sticker_area,0.5,np.where(img_sticker==0,img_sticker,sticker_area).astype(np.uint8), 0.5, 0)
plt.imshow(cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB))
plt.show()
(902, 669)
(x,y) : (902,669)
(w,h) : (322,322)
(x,y) : (741,508)
(1746, 1257)
(x,y) : (1746,1257)
(w,h) : (959,959)
(x,y) : (1267,778)
(482, 669)
(x,y) : (482,669)
(w,h) : (386,386)
(x,y) : (289,476)

  • 고양이 수염의 회전을 생각해서 회전 함수를 사용했지만, 실제로 고양이 수염을 붙였을때 모습이 조금 비뚤어져 보였다.

  • 사람의 코의 각도가 다를 수 있고, 사진에 찍힌 얼굴 각도에 따라도 고양이 수염 회전 함수의 각도가 유용하게 작용하지 않는 것을 알 수 있었다.

회고

  • 어려웠던 점 : cv2.addWeighted을 적용하는 부분이 어려웠다. 사진의 밝기를 조절하는 법이 어려워서 밝았을때, 얼굴을 인식할 수 있는지에 대해서는 확인하지 못했지만, 어두울 때 사람 얼굴을 인식하고 고양이 수염도 붙일 수 있으니 밝았을때도 가능할거라 생각한다.

  • 알아낸 점 및 모호한 점 : 고양이 수염을 회전시켰을 때 함수를 따로 구현할 수 있다는 것을 알아냈다.

  • 노력한 점 : 얼굴 각도에 따른 고양이 수염을 붙이는 법을 해냈다.

  • 자기다짐 : 얼굴이 옆으로 틀어졌을 때, 고양이 수염이 입체적으로 보이도록 붙이는 방법을 찾아서 적용하고 싶다. 각각의 구현한 것을 함수로 짠다면, 일일이 복사해서 사용하지 않을거라 생각한다.